import requests
import json
import hmac
import hashlib
import time
import os
from ..exceptions import PlatformAPIException, PlatformRequestException, PlatformNotSupportSymbolException, PlatformClientException
from ..restapibase import RestAPIBase
from .symbols import symbols
# from tenacity import retry, stop_after_attempt, retry_if_result, retry_if_exception_type, wait_fixed


# def is_sign_error(value):
#     result = False
#     print("in" + json.dumps(value))

#     if 'code' in value and value['code'] == -1022:
#         print("signature error, retry...")
#         result = True
#     return result


class BinanceRESTAPI(RestAPIBase):
    """docstring for BinanceRESTAPI"""

    def __init__(self, api_key, secret_key, isdebug):
        self.api_key = api_key
        self.secret_key = secret_key
        self.isdebug = isdebug
        self.url = 'https://api.binance.com'

    def _debug(self, result):
        if self.isdebug:
            print(result)

    def _get_headers(self):
        headers = {'X-MBX-APIKEY': self.api_key, 'Accept': 'application/json', 'Connection': 'close'}
        return headers

    def _get_signature(self, data={}):
        data.update({'timestamp': int(time.time() * 1000)})
        params_string = "&".join(["%s=%s" % (key, value)
                                  for key, value in data.items()])
        self._debug('params_string: ' + params_string)

        signature = hmac.new(self.secret_key.encode(
            'utf-8'), params_string.encode('utf-8'), hashlib.sha256).hexdigest()
        self._debug(signature)

        data['signature'] = signature
        return data

    def _get(self, endpoint, signed=False, params={}):
        url = self.url + endpoint
        headers = self._get_headers()
        # self._debug(signed)

        if signed:
            params = self._get_signature(params)
        self._debug(params)
        response = requests.get(url, headers=headers, params=params)

        rescode = response.status_code
        restext = response.text
        self._debug(str(rescode) + '|' + restext)
        errmsg = 'StatusCode: GET %s \nURL:%s \nParams: %s \nResponse: %s' % (
            rescode, url, str(params), restext)
        response = response.json()
        self._debug(response)

        if response or (response == []):
            if rescode == 200:
                return response
            elif rescode == 400:
                raise PlatformAPIException(errmsg)
        else:
            raise PlatformRequestException(errmsg)

    def _post(self, endpoint, data={}):
        url = self.url + endpoint
        headers = self._get_headers()
        signed_data = self._get_signature(data)
        self._debug(signed_data)
        response = requests.post(url, headers=headers, data=signed_data)

        rescode = response.status_code
        restext = response.text
        self._debug(str(rescode) + '|' + restext)
        errmsg = 'StatusCode: POST %s \nURL:%s \nData: %s \nResponse: %s' % (
            rescode, url, str(data), restext)
        response = response.json()

        if response or (response == []):
            if rescode == 200:
                return response
            elif rescode == 400:
                raise PlatformAPIException(errmsg)
        else:
            raise PlatformRequestException(errmsg)

    def _del(self, endpoint, data={}):
        url = self.url + endpoint
        headers = self._get_headers()
        signed_data = self._get_signature(data)
        self._debug(signed_data)
        response = requests.delete(url, headers=headers, data=signed_data)
        self._debug(response)

        rescode = response.status_code
        restext = response.text
        self._debug(str(rescode) + '|' + restext)
        errmsg = 'StatusCode: DEL %s \nURL:%s \nParams: %s \nResponse: %s' % (
            rescode, url, str(data), restext)
        response = response.json()

        if response or (response == []):
            if rescode == 200:
                return response
            elif rescode == 400:
                raise PlatformAPIException(errmsg)
        else:
            raise PlatformRequestException(errmsg)

    # EndPoints
    def _order(self, symbol, side, ordertype, amount, price):
        url = '/api/v3/order'

        data = {
            'symbol': symbol,
            'side': side,
            'type': ordertype,
            'quantity': amount,
            'recvWindow': 5000
        }

        if ordertype.lower() == 'limit':
            data['timeInForce'] = 'GTC'
            data['price'] = price

        result = self._post(url, data)
        return result

    def _symbols(self):
        url = '/api/v1/exchangeInfo'

        resjson = self._get(url)

        ratelimit = resjson['rateLimits']

        result = []
        for symbol in resjson['symbols']:
            result.append(symbol['symbol'])

        sw = open(os.path.dirname(
            os.path.realpath(__file__)) + '/symbols.py', 'w')
        sw.write('symbols = ' + str(result))
        sw.close()

        self._debug('symbols: \n' + json.dumps(result, indent=2))
        return result

    # Public
    def get_currency(self, currency):
        return currency.upper()

    def get_symbol(self, commodity, base):
        result = commodity.upper() + base.upper()
        if not (result in symbols):
            errmsg = 'Not supported: %s' % result
            raise PlatformNotSupportSymbolException(errmsg)

        return result
    # NotImplementedError

    def get_ticker(self, symbol):
        raise NotImplementedError()

    def get_depth(self, symbol):
        url = '/api/v1/depth'

        params = {
            'symbol': symbol
        }

        resjson = self._get(url, False, params)

        result = {}
        asks = []
        bids = []

        for ask in resjson['asks']:
            asks.append({'price': float(ask[0]), 'amount': float(ask[1])})

        for bid in resjson['bids']:
            bids.append({'price': float(bid[0]), 'amount': float(bid[1])})

        result = {'bids': bids, 'asks': asks}

        self._debug('server time: \n' + json.dumps(result, indent=2))
        return result

    # NotImplementedError
    def get_account(self):
        raise NotImplementedError()

    # @retry(wait=wait_fixed(3), retry=retry_if_exception_type(PlatformAPIException))
    def get_balances(self, type='trade'):
        url = '/api/v3/account'

        params = {
            'recvWindow': 5000
        }

        resjson = self._get(url, True, params)
        result = {}

        for balance in resjson['balances']:
            result[balance['asset']] = float(balance['free'])

        self._debug('account_info: \n' + json.dumps(result, indent=2))
        return result

    # @retry(stop=stop_after_attempt(1))
    def limit_buy(self, symbol, amount, price):
        result = self._order(symbol, 'BUY', 'LIMIT', amount, price)
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def limit_sell(self, symbol, amount, price):
        result = self._order(symbol, 'SELL', 'LIMIT', amount, price)
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def market_buy(self, symbol, amount, price):
        result = self._order(symbol, 'BUY', 'MARKET', amount, price)
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def market_sell(self, symbol, amount, price):
        result = self._order(symbol, 'SELL', 'MARKET', amount, price)
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def get_order(self, symbol, order_id):
        url = '/api/v3/order'
        params = {
            'symbol': symbol,
            'orderId': order_id
        }

        result = self._get(url, True, params)
        self._debug('get_orders: \n' + json.dumps(result, indent=2))
        return result

    # NotImplementedError
    def get_all_orders(self):
        raise NotImplementedError()

    def get_open_orders(self):
        url = '/api/v3/openOrders'

        result = self._get(url, True)
        self._debug('get_orders: \n' + json.dumps(result, indent=2))
        return result

    def cancel_order(self, symbol, order_id):
        url = '/v1/order/cancel'
        data = {
            'orderId': order_id,
        }

        result = self._del(url, data)
        self._debug(
            'orderid cancel[ ' + str(order_id) + ' ]: ' + json.dumps(result, indent=2))
        return result

    def cancel_all_orders(self):
        orders = self.get_open_orders()

        for order in orders:
            self.cancel_order(order['symbol'], order['orderId'])

        return True

    # NotImplementedError
    def withdraw(self, currency, amount, address):
        raise NotImplementedError()
